﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CuitControlsExtension.cs" company="">
//   
// </copyright>
// <summary>
//   The AST controls extension.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace UITest.CUIT.CuitControls.Extension
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Reflection;

    using Microsoft.VisualStudio.TestTools.UITesting;
    using Microsoft.VisualStudio.TestTools.UITesting.HtmlControls;

    using UITest.CUIT.CuitControls.Exceptions;

    /// <summary>
    ///     The AST controls extension.
    /// </summary>
    public static class CuitControlsExtension
    {
        #region Public Methods and Operators

        /// <summary>
        /// The cuit set filter properties.
        /// </summary>
        /// <param name="testControl">
        /// The test control.
        /// </param>
        /// <param name="filterProperties">
        /// The filter properties.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <exception cref="CuitInvalidFilterParameterFormat">
        /// </exception>
        /// <exception cref="CuitInvalidFilterKey">
        /// </exception>
        public static void CuitSetFilterProperties<T>(this UITestControl testControl, string filterProperties)
        {
            var controlProperties = new List<FieldInfo>();

            Type nestedType = typeof(T);
            Type nestedPropertyNamesType = nestedType.GetNestedType("PropertyNames");

            while (nestedType != typeof(object))
            {
                if (nestedPropertyNamesType != null)
                {
                    controlProperties.AddRange(nestedPropertyNamesType.GetFields());
                }

                nestedType = nestedType.BaseType;
                nestedPropertyNamesType = nestedType.GetNestedType("PropertyNames");
            }

            if (filterProperties == null)
            {
                return;
            }

            // Split on groups of key/value pairs
            string[] arrayKeyValuePairs = filterProperties.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (string keyValue in arrayKeyValuePairs)
            {
                var compareOperator = PropertyExpressionOperator.EqualTo;

                // If split on '=' does not work, then try '~'
                // Split at the first instance of '='. Other instances are considered part of the value.
                string[] arrayKeyVal = keyValue.Split(new[] { '=' }, 2);
                if (arrayKeyVal.Length != 2)
                {
                    // Otherwise try to split on '~'. If it works then compare type is Contains
                    // Split at the first instance of '~'. Other instances are considered part of the value.
                    arrayKeyVal = keyValue.Split(new[] { '~' }, 2);
                    if (arrayKeyVal.Length == 2)
                    {
                        compareOperator = PropertyExpressionOperator.Contains;
                    }
                    else
                    {
                        throw new CuitInvalidFilterParameterFormat(filterProperties);
                    }
                }

                // Find the first property in the list of known values
                string valueName = arrayKeyVal[0];

                if (typeof(T).IsSubclassOf(typeof(HtmlControl))
                    && valueName.Equals("Value", StringComparison.OrdinalIgnoreCase))
                {
                    // support for backward compatibility where filter properties like "Value=Log In" are used
                    valueName += "Attribute";
                }

                FieldInfo foundField =
                    controlProperties.Find(
                        filterProperty => filterProperty.Name.Equals(valueName, StringComparison.OrdinalIgnoreCase));

                if (foundField == null)
                {
                    throw new CuitInvalidFilterKey(
                        valueName, 
                        filterProperties, 
                        controlProperties.Select(x => x.Name).ToList());
                }

                // Add the filter property, value and type
                testControl.FilterProperties.Add(
                    foundField.GetValue(valueName).ToString(), 
                    arrayKeyVal[1], 
                    compareOperator);
            }
        }

        /// <summary>
        /// The cuit set search properties.
        /// </summary>
        /// <param name="testControl">
        /// The test control.
        /// </param>
        /// <param name="searchProperties">
        /// The search properties.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <exception cref="CuitInvalidSearchParameterFormat">
        /// Invalid Search Parameters
        /// </exception>
        /// <exception cref="CuitInvalidSearchKey">
        /// Invalid Search Key
        /// </exception>
        public static void CuitSetSearchProperties<T>(this UITestControl testControl, string searchProperties)
        {
            var controlProperties = new List<FieldInfo>();

            Type nestedType = typeof(T);
            Type nestedPropertyNamesType = nestedType.GetNestedType("PropertyNames");

            while (nestedType != typeof(object))
            {
                if (nestedPropertyNamesType != null)
                {
                    controlProperties.AddRange(nestedPropertyNamesType.GetFields());
                }

                nestedType = nestedType.BaseType;
                nestedPropertyNamesType = nestedType.GetNestedType("PropertyNames");
            }

            if (searchProperties == null)
            {
                return;
            }

            // Split on groups of key/value pairs
            string[] arrayKeyValuePairs = searchProperties.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (string keyValue in arrayKeyValuePairs)
            {
                var compareOperator = PropertyExpressionOperator.EqualTo;

                // If split on '=' does not work, then try '~'
                // Split at the first instance of '='. Other instances are considered part of the value.
                string[] arrayKeyVal = keyValue.Split(new[] { '=' }, 2);
                if (arrayKeyVal.Length != 2)
                {
                    // Otherwise try to split on '~'. If it works then compare type is Contains
                    // Split at the first instance of '~'. Other instances are considered part of the value.
                    arrayKeyVal = keyValue.Split(new[] { '~' }, 2);
                    if (arrayKeyVal.Length == 2)
                    {
                        compareOperator = PropertyExpressionOperator.Contains;
                    }
                    else
                    {
                        throw new CuitInvalidSearchParameterFormat(searchProperties);
                    }
                }

                // Find the first property in the list of known values
                string valueName = arrayKeyVal[0];

                if (typeof(T).IsSubclassOf(typeof(HtmlControl))
                    && valueName.Equals("Value", StringComparison.OrdinalIgnoreCase))
                {
                    // support for backward compatibility where search properties like "Value=Log In" are used
                    valueName += "Attribute";
                }

                FieldInfo foundField =
                    controlProperties.Find(
                        searchProperty => searchProperty.Name.Equals(valueName, StringComparison.OrdinalIgnoreCase));

                if (foundField == null)
                {
                    throw new CuitInvalidSearchKey(
                        valueName, 
                        searchProperties, 
                        controlProperties.Select(x => x.Name).ToList());
                }

                // Add the search property, value and type
                testControl.SearchProperties.Add(
                    foundField.GetValue(valueName).ToString(), 
                    arrayKeyVal[1], 
                    compareOperator);
            }
        }

        /// <summary>
        /// The get control centre.
        /// </summary>
        /// <param name="control">
        /// The control.
        /// </param>
        /// <returns>
        /// The <see cref="Point"/>.
        /// </returns>
        public static Point GetControlCentre(this UITestControl control)
        {
            int horizontalCentre = (control.BoundingRectangle.Location.X + control.Width) - (control.Width / 2);
            int verticalCentre = (control.BoundingRectangle.Location.Y + control.Height) - (control.Height / 2);
            return new Point(horizontalCentre, verticalCentre);
        }

        #endregion
    }
}